/* readWord.h */

/*
    Safe string data entry from file or keyboard ...
    BUT ... free new memory when done with it!
    Don't forget that C strings are pointers to a block of char,
    with a '\0' char at the terminal end ... Call like this:

    char* word = readWord(stdin, 31, "\n\t ,.;:", &lastChr);//note delimiter str

    or ...

    while((word = readWord(FILEp, startChunkSize, delimiterStr, &lastCharRead)))
    {
        // do stuff ...
    }

    Note 1: select your initial word chunk size to minimize calls to realloc
    Note 2: address of last char passed in/out so can flush stdin or file line
*/

#ifndef dwREADWORD
#define dwREADWORD

#ifndef dwSTDIO_H
#define dwSTDIO_H
#include <stdio.h>
#endif
#ifndef dwSTDLIB_H
#define dwSTDLIB_H
#include <stdlib.h>
#endif
#ifndef dwSTRING_H
#define dwSTRING_H
#include <string.h> /* re. strchr */
#endif

#ifndef dwMYASSERT
#define dwMYASSERT
void myAssert( int condition, char text[] )
{
    if( !condition )
    {
        fprintf(stderr, "%s\n", text );
        fputs( "Press 'Enter' to exit program ... ", stderr );
        getchar();
        exit(1);
    }
}
#endif

/* returns a string in new memory holding each word ... or NULL if EOF */
char* readWord( FILE* fp, int chunk_len, char delimits[], char* c)
{
    char *strData, *tmp;
    int i = 0; /* ... *c to hold each char, i for index in string ... */
    if( chunk_len < 1 ) chunk_len = 3; /* set to min. starting chunk size */

    strData = (char*) calloc(chunk_len+1, 1); /* cast added for C++ compilers */
    myAssert( (strData != NULL), "Error: calloc failed to allocate (1)" );

    while( (*c = fgetc( fp )) != EOF && strchr(delimits, *c) ) ; /* advance */

    if( *c != EOF ) strData[i++] = *c; /* ok ... get this first char in the str */
    else return NULL;

    while( (*c = fgetc( fp )) != EOF && !strchr(delimits, *c) )
    {
        if( i == chunk_len-1 ) /* get more memory now ... (for word or line) */
        {
            chunk_len += chunk_len; /* double memory ...*/
            if( (tmp = (char*) realloc(strData, chunk_len+1)) == NULL )
            {
                free( strData );
                myAssert( 0, "Error: realloc failed to allocate (2)" );
            }
            /* if reach here, realloc above was a success, so update strData */
            strData = tmp;
        }
        /* now can ... */
        strData[i] = *c;
        ++i;
    }
    strData[i] = 0; /* confirm termination */

    /* if EOF and empty line, quit reading file, BUT FIRST, free this strData */
    if( *c == EOF && i == 0 ) { free( strData); return NULL; }

    /* else ... */
    if( (tmp = (char*) realloc(strData, i+1)) == NULL ) /* i is index of '\0' */
    {
        free( strData );
        myAssert( 0, "Error: realloc failed to allocate (3)" );
    }
    /* if reach here, realloc above was a success, program NOT aborted, so ...*/
    return tmp;
}

#endif



/* readWord_testProgram.c // needs file readWord.txt in same folder ... // */

#include "readWord.h" /* includes stdio.h, stdlib.h, string.h and myAssert */

#define TEST_FILE "readWord.txt"
#define ARY_SIZE 10

int main()
{
    int i, j;
    char last;
    char* word;
    char* raggedAry[ARY_SIZE];
    char delimits[] = "\n\t ,.;:";
    FILE* fp = fopen( TEST_FILE, "r" );
    myAssert( (fp != NULL), "Error: file '" TEST_FILE "' failed to open." );

    puts( " === FROM FILE '" TEST_FILE "' ===" );
    while( (word = readWord(fp, 31, delimits, &last)) )
    {
        printf( "%s\n", word );
        free( word );
    }
    puts( "===" );

    fclose( fp );

    i = 0;
    printf( "\n === VIA KEY BOARD enter up to %d words ===", ARY_SIZE );
    printf( "\nEnter words (Enter q to quit)  : " );
    while
    (
        i < ARY_SIZE &&
        (word = readWord(stdin, 4, delimits, &last))
    )
    {
        if( ((word[0] == 'q' || word[0] == 'Q') && word[1] == 0) )
        {
            free( word );
            break;
        }
        raggedAry[i++] = word;
    }

    puts( "\nYou entered ..." );
    for( j = 0; j < i; ++j )
    {
        printf( "%2d: %s\n", j+1, raggedAry[j] );
        free( raggedAry[j] );
    }

    fputs( " ===> Press 'Enter' to exit ===> ", stdout );
    /* flush stdin buffer ... */
    while( last != '\n' && last != EOF ) last = getchar();
    getchar();
    return 0;
}

/*  file name: "readWord.txt" */
/*
abd def
ghi,jkl

mn,    opqrst     uv;wxyz,
mn,    opqrst    ;   ;    uv,

wxyz
,


,
*/

/* output ... */
/*
 === FROM FILE 'readWord.txt' ===
abd
def
ghi
jkl
mn
opqrst
uv
wxyz
mn
opqrst
uv
wxyz
===

 === VIA KEY BOARD enter up to 10 words ===
Enter words (Enter q to quit)  :
*/
